home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Interactive Reference Guide
/
C-C++ Interactive Reference Guide.iso
/
c_ref
/
csource3
/
183_01
/
letter.c
< prev
next >
Wrap
C/C++ Source or Header
|
1986-02-04
|
21KB
|
817 lines
/*
* letter A short, savage, document processor designed to serve one
* function; format a one page letter.
*
* I don't know about anybody else, but I write about 25 one-
* pagers a week and I like my letters to look pretty. This
* requires vertical centering and I've not found a word
* processor yet that can do it (although qnxs' document
* processor 'doc' did provide sufficient primitives to
* write a REALLY complex macro that did and UNIXs' nroff and
* a whole bunch of shell commands could do the job as well).
*
* letter will vertically center a one page letter, will
* divert the address to a file (suitable for printing on
* an envelope by address) and support a small set of document
* processing functions. It is weak on error checking and
* semantic analysis (so don't push it!). See the separate
* document 'letter.doc' for more detail.
*
* This program took less than six hours to write. It is
* probably far from perfect. I'm not offering this as a
* replacement for nroff (or even WordStar), but it does
* what I need acceptably.
*
* This is thrown freely into the public domain and can be
* used, modified, and distributed by any person or agency
* that wants to. I would appreciate it if the original
* author credit remains and would also request that no
* compensation be charged by anyone passing this turkey
* around.
*
* This program was written by Jon Simkins and tested using the DeSmet C
* compiler and MS-DOS 3.0.
*
* Revision Notes
*
* Date Revision
* --------------------
* 86.02.04 Added UL_ON and BF_ON control strings for printers that
* directly support underlining and boldface (most if them
* these days).
* Changed setDate() function for Wizard C and recompiled. js.
*
* Copyright (c) 1985 SoftFocus
* 1343 Stanbury Dr.,
* Oakville, Ontario, Canada
* L6L 2J5
* (416)825-0903
*
* Compuserve: 76566, 1172
*/
#include <stdio.h>
#include <bdos.h> /* for getdate() structure */
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#define MAXLINE 200 /* maximum input line. Assumed larger than */
/* maximum print line. */
#define LPP 66 /* maximum lines per page. */
#define ERROR -1
#define HEAD 6 /* lines left at top of page when paper is loaded */
/*
* program defaults and printer controls.
* If your printer cannot backspace one character, don't use underline or
* boldface.
*/
#define DEFIN 0 /* default indent */
#define DEFRM 65 /* default right margin */
#define DEFPO 5 /* default page offset */
#define DEFJU FALSE /* justification off */
#define DEFFI TRUE /* fill on */
#define FF "\014" /* string to formfeed the printer */
#define BACKSPACE 8 /* character to backspace the printer */
/*
* Printer Control Strings
* -----------------------
* If your printer supports such niceties as automatic underlining and
* boldface, then fill in the following defines in the logical manner.
* If they are left undefined, then letter will underline by backspacing
* and printing a '_' and boldface by doublestriking.
* The given examples are for a Brother HR-35 daisywheel. This is disabled
* in the distribution version.
*/
/* #define UL_ON "\033E" *//* enable auto underline */
/* #define UL_OFF "\033R" *//* disable auto underline */
/* #define BF_ON "\033O" *//* enable auto boldface */
/* #define BF_OFF "\033&" *//* disable auto boldface */
FILE *fd; /* main input file */
FILE *fdAdd; /* address file */
FILE *printer; /* printer device */
int hardCopy; /* true if printed */
#define PRINTER "LPT1" /* hardcopy output */
#define CONSOLE "CON" /* console output */
/*
* letter array, line buffer, and counters
*/
char *lines[LPP]; /* build the page here */
int lineCount;
char pLine[MAXLINE]; /* build the current line here */
int pCount;
int saveJu, saveFi;
/*
* command array & defines
*/
#define COMCOUNT 13
char *commands[] = {". ", "in", "po", "ju", "rm", "ti", "br",
"as", "ae", "sp", "dt", "sg", "ce", "fi"};
#define COMMENT 0
#define INDENT 1
#define PAGEOFF 2
#define JUSTIFY 3
#define RM 4
#define TI 5
#define BR 6
#define AS 7
#define AE 8
#define SP 9
#define DT 10
#define SG 11
#define CENTER 12
#define FILL 13
/*
* margins, switches.
* There is currently no way to disable the interpretation of the characters
* listed below.
*/
#define BC '^' /* boldface toggle */
#define UC '_' /* underline toggle */
#define HS '~' /* 'hard' space (not spread by justification) */
int rm, in, ti, ju, po, fi, br, sp, ce;
int addFlag; /* whether address redirection in effect */
int ul, bf; /* underline, boldface currently selected */
/*
* off and away
*/
main(argc, argv)
int argc;
char *argv[];
{
char buff[MAXLINE], word[MAXLINE];
int index, len;
char *pr;
register j;
/*
* check argument count
*/
if (argc > 3 || argc < 2)
erExit("usage: letter [-p] <fName>");
/*
* get command line option(s)
*/
j=1;
if (argv[j][0] == '-' && argv[j][1] == 'p') {
pr = PRINTER;
hardCopy = TRUE;
j++;
}
else {
pr = CONSOLE;
hardCopy = FALSE;
}
/*
* open input file
*/
if ( (fd=fopen(argv[j], "r")) == 0) {
printf("Could not open '%s'\n", argv[j]);
exit(ERROR);
}
/*
* set defaults
*/
ul = bf = addFlag = FALSE;
in = DEFIN;
rm = DEFRM;
ju = DEFJU;
fi = DEFFI;
if (hardCopy)
po = DEFPO;
else
po = 0;
ti = ce = lineCount = 0;
/*
* input/process loop
*/
pCount = 0;
pLine[0] = '\0';
while( fgets(buff, MAXLINE, fd) != NULL) {
trim(buff);
/*
* blank line?
*/
if (buff[0] == '\0') {
pFlush(FALSE);
pCount=1;
pFlush(FALSE);
continue;
}
/*
* check for any command lines
*/
if (buff[0] == '.') {
dotComm(buff);
continue;
}
/*
* if fill mode is off, just pass the line on through.
*/
if ( !(fi)) {
strcpy(pLine, buff);
pCount = lenCal(pLine);
pFlush(ju);
}
else {
/*
* must be text; add it (word at a time) to the current print
* line.
*/
index=0;
do {
index = getWord(index, buff, word);
if (index == 0)
continue;
len = lenCal(word);
/*
* if there's still room, add word to current line
*/
if ( ( len + pCount) < (rm-in-ti) ) {
if (pCount != 0) {
strcat( pLine, " ");
pCount++;
}
strcat( pLine, word);
pCount += len;
}
/*
* 'print' current line and start new one.
*/
else {
pFlush(ju);
strcpy(pLine, word);
pCount=len;
}
} while (index);
}
}
pFlush(FALSE);
fclose(fd);
/*
* all text is in internal text buffer nicely formatted.
* Now center it on the page and print it.
*/
/*
* strip off any trailing blank lines
*/
while ( trim(lines[lineCount][0]) == '\0')
lineCount--;
/*
* center the text vertically
*/
printer = fopen(pr, "w");
len = ( (LPP-lineCount) / 2) - HEAD + 1;
if (hardCopy)
for(j=0; j<=len; j++)
lprint("\n");
/*
* ... and print it all
*/
for(j=0; j<= lineCount; j++) {
lprint(lines[j]);
lprint("\015\n");
free(lines[j]);
}
if (hardCopy)
lprint(FF);
fclose(printer);
}
/*
* trim Strip off all trailing white spaces
*/
trim(str)
char *str;
{
register j;
for( j=strlen(str); (str[j] <= ' ') && (j > -1); j--);
str[++j] = '\0';
}
/*
* strip Pull off all leading spaces.
* Stripped string is returned in place.
*/
strip(str)
char *str;
{
register j, k;
for(j=0; (str[j] <= ' ') && (str[j]); j++);
if ( !(j) )
return;
k=0;
do {
str[k++] = str[j];
} while (str[j++]);
}
/*
* erExit Print an error message and die
*/
erExit(str)
char *str;
{
printf("%s\n", str);
exit(ERROR);
}
/*
* getWord Pull the next word off the passed string. Return an index into
* the source string pointing to the next word.
*/
getWord(index, buff, word)
int index;
char *buff, *word;
{
register j = 0;
while ( buff[index] <= ' ' ) {
if (buff[index] == '\0') {
word[0] = '\0';
return 0;
}
index++;
}
while ( (buff[index] != ' ') && (buff[index] != '\0') )
word[j++] = buff[index++];
word[j++] = '\0';
return index;
}
/*
* pFlush Add the current print line to lines[].
*/
pFlush(just)
int just;
{
register j;
char *malloc();
if (pCount == 0)
return;
if (lineCount == LPP)
erExit("Page overflow! This utility only works for one page letters.");
/*
* insert page offset, indent
*/
trim(pLine);
if (ce) {
strip(pLine);
center(pLine, rm-in-ti);
ce--;
}
else
if (just)
justify(pLine, rm-in-ti);
lines[lineCount] = malloc( MAXLINE );
for(j=0; j<po+in+ti; j++)
lines[lineCount][j] = ' ';
ti=0;
strcpy( &lines[lineCount++][j], pLine);
if (addFlag)
fprintf(fdAdd, "%s\n", pLine);
pCount = 0;
pLine[0] = '\0';
}
/*
* justify Right justify a line in place. This is a fairly sleazy
* algorithm, but I don't often need justification. Hackers
* note!
*/
justify(line, margin)
char *line;
int margin;
{
int lp=0, rp=0, sp, len;
len=strlen(line);
margin += len - lenCal(line);
sp = margin - len;
lp=0; rp=len;
while (sp) {
/*
* find first space from current left position
*/
while(line[lp] != ' ') {
if (line[lp] == '\0') {
lp=0;
break;
}
lp++;
}
/*
* add a space and find next non-blank
*/
if (lp) {
addSpace(line, lp, 1);
len++;
sp--;
while ( (line[lp] <=' ') && (line[lp]) )
lp++;
}
if (sp) {
while (line[rp] != ' ') {
rp--;
if (rp < 0) {
rp=len;
break;
}
}
if (rp != len) {
addSpace(line, rp, 1);
len++;
sp--;
while (line[rp] <=' ' && (rp > -1) )
rp--;
if (rp < 0)
rp=len;
}
}
}
}
/*
* Center Center the given string in place using the supplied
* line length.
*/
center(str, len)
char *str;
int len;
{
len = (len - lenCal(str)) / 2;
addSpace(str, 0, len);
}
/*
* addSpace Add <count> spaces at the given position.
*/
addSpace(str, pos, count)
char *str;
int pos;
int count;
{
register j;
if (count <= 0)
return;
for ( j=strlen(str)+count; j>pos; j--)
str[j] = str[j-count];
for (j=0; j<count; j++)
str[pos++]=' ';
}
/*
* dotComm Process a command line
*/
dotComm(buff)
char *buff;
{
char scratch[3];
register j;
int action = ERROR;
int relative, value;
for(j=1; j<3; j++)
scratch[j-1] = buff[j];
scratch[2] = '\0';
for(j=0; j <= COMCOUNT; j++) {
if (strcmp(commands[j], scratch) == 0) {
action = j;
break;
}
}
if (action == ERROR)
return;
if (buff[4] == '+' || buff[4] == '-') {
relative = TRUE;
j=atoi(&buff[5]);
if (buff[4] == '-')
j *= -1;
}
else {
relative = FALSE;
j = atoi(&buff[4]);
}
switch (action) {
case COMMENT:
break;
case INDENT:
if (relative)
value = in+j;
else
value = j;
if ( !(value <0 || value >rm)) {
pFlush(FALSE);
in = value;
}
break;
case TI:
if (relative)
ti = j;
else
ti = in - j;
pFlush(FALSE);
break;
case PAGEOFF:
if ( !(hardCopy) )
break;
if (relative)
value = po+j;
else
value = j;
if ( !(value<0 || value > 100)) {
pFlush(FALSE);
po = value;
}
break;
case CENTER:
pFlush(FALSE);
strip(&buff[3]);
ce = j;
if (j == 0 && (buff[3] == '\0') )
ce = 1;
break;
case JUSTIFY:
pFlush(FALSE);
strip(&buff[3]);
trim( &buff[3]);
if (strcmp( &buff[3], "on") )
ju = FALSE;
else
ju = TRUE;
break;
case RM:
if (relative)
value = rm+j;
else
value = j;
if ( !(value<10 || value >100) ) {
pFlush(FALSE);
rm=value;
}
break;
case BR:
pFlush(FALSE);
break;
case AS:
if (addFlag)
break;
pFlush(FALSE);
saveFi = fi;
saveJu = ju;
fi = FALSE;
ju = FALSE;
if ( (fdAdd = fopen("address", "w")) == NULL)
erExit("Could not open address file.");
addFlag = TRUE;
break;
case AE:
if (addFlag != TRUE)
erExit("Address redirection not in effect!");
fclose(fdAdd);
pFlush(FALSE);
fi = saveFi;
ju = saveJu;
addFlag = FALSE;
break;
case FILL:
pFlush(FALSE);
strip(&buff[3]);
trim( &buff[3]);
if (strcmp( &buff[3], "on") )
fi = FALSE;
else
fi = TRUE;
break;
case SP:
pFlush(FALSE);
value=0;
do {
pCount=1;
pFlush(FALSE);
} while (++value < j);
break;
case DT:
pFlush(FALSE);
setDate(TRUE);
pFlush(FALSE);
break;
case SG:
pFlush(FALSE);
strcpy(pLine, "Sincerely yours,");
pCount=1;
pFlush(FALSE);
pCount=1;
pFlush(FALSE);
pCount=1;
pFlush(FALSE);
pCount=1;
pFlush(FALSE);
trim(&buff[4]);
strip(&buff[4]);
strcpy(pLine, &buff[4]);
pCount=1;
pFlush(FALSE);
break;
default:
break;
}
}
/*
* setDate Fill in the current system date in pLine.
* The DeSmet C function dates() is used here to snag
* the system date; other compilers will need other functions.
*/
setDate(right)
int right;
{
int month, day, year;
struct date db;
char dt[25];
register j;
static char *mTable[] = {"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"};
getdate(&db);
month = db.da_mon;
day = db.da_day;
year = db.da_year;
sprintf(dt, "%s %d, %d", mTable[month-1], day, year);
if (right) {
for(j=0; j<rm; j++)
pLine[j] = ' ';
strcpy( &pLine[ rm - strlen(dt) ], dt);
}
else
strcpy(pLine, dt);
pCount = strlen(pLine);
}
/*
* lenCal Calculate the length of a string disregarding boldface
* and underline command characters.
*/
lenCal(str)
char *str;
{
register j, len;
for(j=0, len=0; str[j] != '\0'; j++)
if ( (str[j] != UC) && (str[j] != BC) )
len++;
return len;
}
/*
* lprint Print the string; enable/disable necessary print controls
*/
lprint(str)
char *str;
{
register j;
/*
* some printers disable all print attributes at the beginning
* of each new line. To handle this, boldface and underline are
* reasserted at the beginning of each print line if they're
* currently enabled.
*/
#ifdef UL_ON
if (ul)
pstr(UL_ON);
#endif
#ifdef BF_ON
if (bf)
pstr(BF_ON);
#endif
/*
* print the line
*/
for(j=0; str[j]; j++) {
switch (str[j]) {
case UC:
if (ul) {
#ifdef UL_ON
pstr(UL_OFF);
#endif
ul=FALSE;
}
else {
#ifdef UL_ON
pstr(UL_ON);
#endif
ul=TRUE;
}
break;
case BC:
if (bf) {
#ifdef BF_ON
pstr(BF_OFF);
#endif
bf = FALSE;
}
else {
#ifdef BF_ON
pstr(BF_ON);
#endif
bf = TRUE;
}
break;
case HS:
putc(' ', printer);
break;
default:
if ( (str[j] > ' ') ) {
#ifndef BF_ON
if (bf) {
putc(str[j], printer);
putc(BACKSPACE, printer);
}
#endif
#ifndef UL_ON
if (ul) {
putc('_', printer);
putc(BACKSPACE, printer);
}
#endif
}
putc(str[j], printer);
break;
}
}
}
/*
* pstr Print the given string to the hardcopy device or not at all
* if we're hitting the console.
*/
pstr(str)
char *str;
{
if ( !(hardCopy) )
return;
while (*str)
putc(*str++, printer);
}